<?php

class FormUtils
{
	static public function name2title ($name)
	{
		$parts = explode('_', $name);
		if ($parts) {
			$parts[0] = ucfirst($parts[0]);
		}
		return implode(' ', $parts);

	} // end of method name2title
}

class FormField
{
	public $ready = false;

	public function __construct ($config=array(), $init=true)
	{
		foreach ($config as $k=>$v) $this->$k = $v;
		if ($init) $this->init();
	}
	public function init($force=false)
	{
		if ($this->ready && !$force) return $this->ready;
		return $this->ready = $this->_init();
	}
	public function _init()
	{
		if (isset($this->fields)) {
			foreach ($this->fields as $fn => $fc) {
				$this->addField($fn, $fc);
			}
		}
		if (isset($this->buttons)) {
			foreach ($this->buttons as $fn => $fc) {
				$this->addButton($fn, $fc);
			}
		}
		$this->initNode();
		return true;
	}
	public function initNode ()
	{
		if (!isset($this->title)) $this->title = FormUtils::name2title($this->name);
		if (!isset($this->description)) $this->description = NULL;
		if (!isset($this->status)) $this->status = NULL;
		if (!isset($this->status_message)) $this->status_message = NULL;
		if (!isset($this->value)) {
			if (isset($this->default)) { $m = $this->default; $this->value = is_callable($m) ? call_user_func($m, $this) : $m; }
			else { $this->value = NULL; }
			if (!isset($this->event)) $this->event = 'create';
		} else {
			if (!isset($this->event)) $this->event = 'update';
		}
	} // end of method _initField

	public function addField ($name, $field)
	{
		if (is_array($field)) {
				$field['name'] = $name;
				$class = isset($field['type']) ? $field['type'].'FormField' : $field['class'];
				$field = new $class($field, false);
		}
		if (!isset($name)) $name = $field->name;
		$field->_parent = $this;
		$field->_form = $this->_form;
		$field->_path = $this->_path .$field->name .'/';
		$this->form['hash'][$field->_path] = $field;
		$this->fields[$field->name] = $field;
		$field->init();
		return $field;
	} // end of method addField
	public function addButton ($name, $field)
	{
		if (is_array($field)) {
				$field['name'] = $name;
				$class = isset($field['type']) ? $field['type'].'FormButton' : $field['class'];
				$field = new $class($field, false);
		}
		if (!isset($name)) $name = $field->name;
		$field->_parent = $this;
		$field->_form = $this->_form;
		$field->_path = $this->_path .$field->name .'/';
		//$this->form['hash'][$field->_path] = $field;
		$field->init();
		$this->buttons[$field->name] = $field;
		return $field;
	} // end of method addField

	public function process ()
	{
		if (isset($this->fields)) {
			foreach ($this->fields as &$f) {
				$f->process();
			}
		}
		$this->processNode();
	} // end of method process
	public function processNode ()
	{
		$has_value = isset($this->value);
		$has_new_value = isset($this->new_value);

	// save old field 'value' as 'old_value'
		if ($has_value) {
			$this->old_value = $this->value;
		}

	// set new field 'value', if exists
		if ($has_new_value) {
			$is_validation_needed = ($this->value !== $this->new_value);
			$this->value = $this->new_value;
		} else {
			$is_validation_needed = !isset($this->value);
		}

	// validate field, if needed
		if (isset($this->validator) && $is_validation_needed) {
			$f = $this->validator;
			$is_invalid = call_user_func($f, $this);
		} else {
			$is_invalid = false;
		}
		if ($is_invalid) {
			$this->status = 'error';
			$this->status_message = $is_invalid;
			$this->_form->status = $this->status;
		}

	// put current (default/old/new) field 'value' in the form hash
		if (property_exists($this, 'value') && isset($this->name)) {
			$this->_form->vhash[$this->name] = $this->value;
		}

		// if valid save data
		if ($this->_form->status != 'error' && isset($this->saver)) {
			$f = $this->saver;
			$is_save_failed = call_user_func($f, $this);
		} else {
			$is_save_failed = false;
		}
		if ($is_save_failed) {
			$this->status = 'error';
			$this->status_message = $is_save_failed;
			$this->_form->status = $this->status;
		}
	} // end of method processField

	public function getEvent ()
	{
		if (isset($this->event)) return $this->event;
		elseif (isset($this->_parent)) return $this->_parent->getEvent();
		else return 'Ok';
	} // end of method getEvent
} // end of class FormField

class ImageFormButton extends FormField
{
}

class Form extends FormField
{
	public function _init ()
	{
		$this->vhash = array();
		$this->_form = $this;
		$this->_path = isset($this->_path) ? $this->_path : '/';
		$this->status = 'ok';
		parent::_init();
		return true;
	} // end of method _init
}

class FieldsetFormField extends FormField
{
}
class TextFormField extends FormField
{
	public function processNode ()
	{
		if (isset($_POST['_'.$this->name.'_default']) && isset($this->default)) {
			$m = $this->default;
			$this->new_value = is_callable($m) ? call_user_func($m, $this) : $m;
			$this->_form->status = 'error';
		} else {
			$this->new_value = isset($_POST[$this->name]) ? $_POST[$this->name] : NULL;
		}
		parent::processNode();
	} // end of method processNode
}
class HiddenFormField extends FormField
{
	public function processNode ()
	{
		$this->new_value = isset($_POST[$this->name]) ? $_POST[$this->name] : NULL;
		parent::processNode();
	} // end of method processNode
}
class PasswordFormField extends FormField
{
	public function processNode ()
	{
		// поле существует и его пытаются изменить?
		if (isset($_POST[$this->name]) && $_POST[$this->name]) {
			$this->new_value = $_POST[$this->name];
			parent::processNode();
		}
	} // end of method processNode
}

class PasswordEditFormField extends FormField
{
	public function processNode ()
	{
		// поле существует и его пытаются изменить?
		if (isset($_POST[$this->name]) && $_POST[$this->name]) {
			$name  = $this->name;
			$old   = "_{$name}_old";
			$check = "_{$name}_2";
			if ($_POST[$name] !== $_POST[$check]) {
				$this->_form->status = $this->status = 'error';
				$this->status_message = 'Пароли не совпадают';
			}
			$this->new_value = $_POST[$name];
			$this->old_value_check = $_POST[$old];
			parent::processNode();
		}
	} // end of method processNode
}

class BooleanFormField extends FormField
{
	public function processNode ()
	{
		if (isset($_POST[$this->name])) {
			$this->new_value = true;
		} else {
			$this->new_value = false;
		}
		parent::processNode();
	} // end of method processNode
}
